package hibernatemock.persistence;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

public class PersistenceUtil {

	private EntityManagerFactory entityManagerFactory;
	private PrepareIds prepareIds = new PrepareIds();
	private DependencyManager dependencyManager = new DependencyManager();

	public PersistenceUtil(EntityManagerFactory entityManagerFactory) {
		this.entityManagerFactory = entityManagerFactory;
	}

	public Object persist(Object rootEntity) {
		EntityManager entityManager = null;
		try {
			if (entityManagerFactory == null)
				return rootEntity;
			entityManager = prepareAndBegin(rootEntity);
			Set<Object> tryList = dependencyManager.all(rootEntity);
			Set<Object> goodList = new LinkedHashSet<Object>();
			while (tryList.size() > 0) {
				Iterator<Object> it = tryList.iterator();
				Object obj = it.next();
				it.remove();
				try {
					entityManager.persist(obj);
					goodList.add(obj);
				} catch (Exception e) {
					rollback(entityManager);
					close(entityManager);
					entityManager = prepareAndBegin(rootEntity);
					tryList.add(obj);
					Set<Object> temp = new LinkedHashSet<Object>();
					temp.addAll(goodList);
					temp.addAll(tryList);
					tryList = temp;
					goodList.clear();
				}
			}
			entityManager.getTransaction().commit();
			return rootEntity;
		} catch (Exception e) {
			rollback(entityManager);
			throw new RuntimeException(e);
		} finally {
			close(entityManager);
		}
	}

	private EntityManager prepareAndBegin(Object rootEntity) {
		EntityManager entityManager;
		entityManager = entityManagerFactory.createEntityManager();
		prepareIds.prepare(rootEntity, entityManager);
		entityManager.getTransaction().begin();
		return entityManager;
	}

	private void close(EntityManager entityManager) {
		if (entityManager != null && entityManager.isOpen())
			entityManager.close();
	}

	private void rollback(EntityManager entityManager) {
		if (entityManager != null && entityManager.getTransaction().isActive())
			entityManager.getTransaction().rollback();
	}

}
